/*
 * net_c.h
 *
 *  Created on: Sep 26, 2013
 *      Author: killerg
 */

#ifndef NET_C_H_
#define NET_C_H_

#include "net_buffer.h"
#include "msg_coder.h"

#include <functional>
#include <sys/socket.h>
#include <event2/bufferevent.h>
#include <string>
#include <assert.h>
#include <string.h>


struct event_base;
struct bufferevent;


struct tran_c {
    const static int MAX_MSG_INDEX = 1000000;
    const static int INIT_MSG_INDEX = 0;

    bool self_base;
    event_base* base;
    bufferevent* bev;

    std::string address;
    sockaddr addr;
    int addr_len;
    bool nodelay;

    bool connected;
    bool connecting;
    int send_index;

    typedef std::function<void()> handler_t;

    handler_t open_cb;
    handler_t close_cb;
    handler_t error_cb;
    code_cb_t code_cb;

    tran_c();
    ~tran_c();
    bool init(event_base* base = NULL, code_cb_t code_cb = NULL, bool nodelay = true);
    void destroy();
    void update();

    bool connect(
            const char* address,
            bool block,
            handler_t open_cb = NULL,
            handler_t close_cb = NULL,
            handler_t error_cb = NULL);

    void disconnect();
    bool is_connected() { return connected; }
    bool is_connecting() { return connecting; }


    template<typename MsgT>
    bool send(MsgT* msg_) {
        assert(is_connected());
        if (!is_connected())
            return false;

        auto output = bufferevent_get_output(bev);
        if (NULL == output)
            return false;

        bool ok = evbuffer_send<MsgT>(output, msg_, NULL, send_index, code_cb);
        if (ok) {
            if (++send_index > MAX_MSG_INDEX)
                send_index = INIT_MSG_INDEX;
        }

        return ok;
    }

    template<typename MsgT>
    MsgT* recv() {
        if (!is_connected())
            return NULL;

        auto input = bufferevent_get_input(bev);
        if (NULL == input)
            return NULL;

        return evbuffer_recv<MsgT>(input);
    }

    size_t write_line(const char* buffer) {
        assert(is_connected());
        if (!is_connected())
            return 0;

        auto output = bufferevent_get_output(bev);
        if (NULL == output)
            return 0;

        return evbuffer_add_printf(output, "%s\n", buffer);
    }

    size_t read_line(char* buffer) {
        if (!is_connected())
            return 0;

        auto input = bufferevent_get_input(bev);
        if (NULL == input)
            return 0;

        size_t n_read_out;
        char* buff = evbuffer_readln(input, &n_read_out, EVBUFFER_EOL_CRLF);
        if (buff) {
            strncpy(buffer, buff, n_read_out);
            free(buff);
        }

        return n_read_out;
    }


    void _try_connect();
};

#endif /* NET_C_H_ */
